#include "VertexSelector.hpp"
#include "gl.hpp"
#include <Fl/Fl.H>
#include "Box.hpp"
#include "Controller.hpp"
#include "GlWindow.hpp"
#include "Mat4.hpp"
#include "MeshAlgorithm.hpp"
#include "MeshView.hpp"
#include "PolyMesh.hpp"
#include "ViewScene.hpp"
#include "Camera.hpp"
#include "MeshAdjacency.hpp"
#include <boost/bind.hpp>


VertexSelector::VertexSelector() : mBoxStarted(false)
{
   mSelectType = cNORMAL;
   mBoxReady = false;
}

int VertexSelector::handle(int event)
{
   if (mBoxReady)
   {
      return handleBox(event);
   }

   int r = 0;
   if (! mBoxStarted)
   {
      if (event == FL_KEYDOWN)
      {
         if (Fl::event_key() == 'b' && !mBoxStarted)
         {
            mBoxReady = true;
            SetSelectOperation();
            r = 1;
         }
         else if (Fl::event_key() == 'a')
         {
            SelectAll();
            MEvents("Redraw");
         }
      }
      if (event == FL_PUSH && Fl::event_button1())
      {
         r = 2;
         mDownPoint.x = (float)Fl::event_x();
         mDownPoint.y = (float)Fl::event_y();
      }
      if (event == FL_RELEASE)
      {
         if ( (mDownPoint.x - (float)Fl::event_x() == 0.0)
            && (mDownPoint.y - (float)Fl::event_y() == 0.0) )
         {
            ViewScene::SelectionMode selMode = ViewScene::GetInstance().GetSelectionMode();
            GlWindow* windowPtr = Controller::GetInstance().mGlWindow;
            MeshView* view = ViewScene::GetInstance().GetSelectedMesh();
            if ( (selMode == ViewScene::cINTERFACE
               || selMode == ViewScene::cFACE
               || selMode == ViewScene::cEDGE_PATCH)
               && view)
            {
               r = 2;
               float xScale(windowPtr->w() / 2.0f), yScale(-windowPtr->h()/2.0f);
               Mat4 worldToScreen = ViewScene::GetInstance().mTransform.GetFullTransform();
               Mat4 screenToWorld = worldToScreen.inverse();
               Mat4 test = worldToScreen * screenToWorld;
               mDownPoint.x = mDownPoint.x / xScale - 1;
               mDownPoint.y = mDownPoint.y / yScale + 1; 
               Vec3 cursor(mDownPoint.x, mDownPoint.y, 1.0f);
               Vec3 camera = Controller::GetInstance().GetCamera()->mViewPoint.Origin();
               //Vec3 camera(0, 0, -2.0f);
               cursor = screenToWorld * cursor;
               //camera = screenToWorld * camera;
               Vec3 v = cursor - camera;

               if (selMode == ViewScene::cINTERFACE)
               {
                  SelectInterface(camera, v, *view);
               }
               else if (selMode == ViewScene::cFACE)
               {
                  SelectFace(camera, v, *view);
               }
               else if (selMode == ViewScene::cEDGE_PATCH)
               {
                  SelectEdgePatch(camera, v, *view);
               }
            }
            else
            {
               SetSelectOperation();
               r = 2;
               
               Mat4 worldToScreen = ViewScene::GetInstance().mTransform.GetFullTransform();
               float xScale(windowPtr->w() / 2.0f), yScale(windowPtr->h()/2.0f);
               mDownPoint.y = windowPtr->h() - mDownPoint.y;
               mDownPoint.x = mDownPoint.x / xScale - 1;
               mDownPoint.y = mDownPoint.y / yScale - 1; 
               //Vec3 p2 = screenToWorld * Vec3(mDownPoint.x, mDownPoint.y, 1);
               //Vec3 v = p2 - p;
               if (view)
               {
                  float bestMetric = 1.0E9;
                  int bestVert = -1;
                  VertexList& verts = *view->mMesh.verts;
                  for (size_t i = 0; i < verts.size(); ++i)
                  {
                     Vec3& vert = verts[i];
                     Vec3 screenVert = vert * worldToScreen;
                     Vec2 screenPt(screenVert[0], screenVert[1]);
                     float metric = (screenPt - mDownPoint).length_squared() + screenVert.z/1000.0f;
                     if (metric < bestMetric)
                     {
                        bestMetric = metric;
                        bestVert = i;
                     }
                  }
                  if (bestVert != -1)
                  {
                     std::vector<int> best;
                     best.push_back(bestVert);
                     SelectFromVerts(*view, best);
                  }
               }
            }
         }
      }
   }
   return r;
}

void VertexSelector::SetSelectOperation()
{
   mSelectType = VertexSelector::cNORMAL;
   if (Fl::event_ctrl())
   {
      mSelectType = VertexSelector::cAND;
   }
   else if (Fl::event_shift())
   {
      mSelectType = VertexSelector::cOR;
   }
}

void VertexSelector::SelectFromVerts(MeshView&         aMesh,
                                     std::vector<int>& aVerts)
{
   ViewScene::SelectionMode mode = ViewScene::GetInstance().GetSelectionMode();
   if (mode == ViewScene::cVERTEX)
   {
      SelectVerts(aMesh, aVerts);
   }
   else if (mode == ViewScene::cLOOP)
   {
      SelectLoops(aMesh, aVerts);
   }
}

int VertexSelector::handleBox(int event)
{
   int r = 0;
   if (event == FL_PUSH && Fl::event_button1())
   { 
      mBoxStarted = true;
      r = 1;
      mDownPoint.x = (float)Fl::event_x();
      mDownPoint.y = (float)Fl::event_y();
   }
   if (event == FL_RELEASE && !Fl::event_button1())
   {
      GlWindow* windowPtr = Controller::GetInstance().mGlWindow;
      mDownPoint.y = windowPtr->h() - mDownPoint.y;
      mUpPoint.y = windowPtr->h() - mUpPoint.y;
      Box selBox(mDownPoint, mUpPoint);
      float xScale(windowPtr->w() / 2.0f), yScale(windowPtr->h()/2.0f);
      selBox.min_x = selBox.min_x / xScale - 1;
      selBox.max_x = selBox.max_x / xScale - 1;
      selBox.min_y = selBox.min_y / yScale - 1;
      selBox.max_y = selBox.max_y / yScale - 1;

      Mat4 worldToScreen = ViewScene::GetInstance().mTransform.GetFullTransform();
      std::vector<int> selection;
      MeshView* mesh = ViewScene::GetInstance().GetSelectedMesh();
      if (mesh)
      {
         PolyMesh& polyMesh = mesh->mMesh;
         const VertexList& verts = *polyMesh.verts;
         for (size_t i = 0; i < verts.size(); ++i)
         {
            const Vec3 v = verts[i];
            Vec3 sv = v * worldToScreen;

            if (selBox.Contains(Vec2(sv.x, sv.y)))
            {
               selection.push_back(i);
            }
         }
         SelectFromVerts(*mesh, selection);
      }
      mBoxReady = mBoxStarted = false;
   }
   if (event == FL_DRAG && Fl::event_button1())
   {
      mBoxStarted = true;
      r = 1;
      mUpPoint.x = (float)Fl::event_x();
      mUpPoint.y = (float)Fl::event_y();
   }
   return r;
}

void VertexSelector::SelectVerts(MeshView&         aMesh,
                                 std::vector<int>& aVerts)
{
   std::vector<int>& selectedVertices = aMesh.GetSelectedVertices();
   if (mSelectType == VertexSelector::cNORMAL)
   {
      selectedVertices = aVerts;
   }
   else if (mSelectType == VertexSelector::cAND)
   {
      std::vector<int> final;
      std::set_intersection(aVerts.begin(), aVerts.end(), selectedVertices.begin(), selectedVertices.end(), std::back_inserter(final));
      selectedVertices = final;
   }
   else
   {
      std::vector<int> final;
      std::set_union(aVerts.begin(), aVerts.end(), selectedVertices.begin(), selectedVertices.end(), std::back_inserter(final));
      selectedVertices = final;
   }
   aMesh.SelectionUpdated();
}

void VertexSelector::SelectLoops(MeshView&         aMesh,
                                 std::vector<int>& aVerts)
{
   std::set<int> selPoints(aVerts.begin(), aVerts.end());
   CreaseStructure& creases = aMesh.GetCreases();
   std::vector<int> selLoops;
   for (size_t i = 0; i < creases.mLoops.size(); ++i)
   {
      VertexLoop& loop = *creases.mLoops[i];
      for (size_t j = 0; j < loop.mVertices.size(); ++j)
      {
         if (selPoints.find(loop.mVertices[j]) != selPoints.end())
         {
            selLoops.push_back(i);
            break;
         }
      }
   }

   if (mSelectType == VertexSelector::cNORMAL)
   {
      aMesh.mSelectedLoops = selLoops;
   }
   else if (mSelectType == VertexSelector::cAND)
   {
      std::vector<int> final;
      std::set_intersection(selLoops.begin(), selLoops.end(), aMesh.mSelectedLoops.begin(), aMesh.mSelectedLoops.end(), std::back_inserter(final));
      aMesh.mSelectedLoops = final;
   }
   else
   {
      std::vector<int> final;
      std::set_union(selLoops.begin(), selLoops.end(), aMesh.mSelectedLoops.begin(), aMesh.mSelectedLoops.end(), std::back_inserter(final));
      aMesh.mSelectedLoops = final;
   }
   aMesh.SelectionUpdated();
}
int VertexSelector::GetHitFace(Vec3      p,
                               Vec3      v,
                               MeshView& aMesh)
{
   std::vector<std::pair<float, int> > hitFaces;
   MeshAlgorithm::FindRayHits(aMesh.mMesh.GetCreaseMesh(), p, v.normal(), hitFaces);
   float nearBoundary = ViewScene::GetInstance().mTransform.mNearPlane;
   const MeshViewConfig& config = aMesh.GetConfig();
   for (size_t i = 0; i < hitFaces.size(); ++i)
   {
      Face& f = aMesh.mMesh.crease_faces[hitFaces[i].second];
      if (hitFaces[i].first < nearBoundary) continue;
      if (std::find(config.mHiddenRegions.begin(), config.mHiddenRegions.end(), f.materials[0]) == config.mHiddenRegions.end()
         || std::find(config.mHiddenRegions.begin(), config.mHiddenRegions.end(), f.materials[1]) == config.mHiddenRegions.end())
      {
         return hitFaces[i].second;
      }
   }
   return -1;
}

void VertexSelector::SelectFace(Vec3      p,
                                Vec3      v,
                                MeshView& aMesh)
{
   int face = GetHitFace(p,v,aMesh);
   if (face >= 0)
   {
      IndexList selFaces;
      if (Fl::event_ctrl())
      {
         selFaces = aMesh.GetSelectedFaces();
      }
      IndexList::iterator iter = std::find(selFaces.begin(), selFaces.end(), face); 
      
      if (iter == selFaces.end())
      {
         selFaces.push_back(face);
      }
      else
      {
         selFaces.erase(iter);
      }
      aMesh.SetSelectedFaces(selFaces);
   }
}

Edge VertexSelector::GetHitEdge(Vec3      p,
                                Vec3      vec,
                                MeshView& aMesh)
{
   using namespace MeshAlgorithm;
   Edge hitEdge(-1,-1);
   int face = GetHitFace(p,vec,aMesh);
   if (face >= 0)
   {
      
      Face& f = aMesh.mMesh.crease_faces[face];
      Vec3 a = aMesh.mMesh.verts[f.vertex[0]],
           b = aMesh.mMesh.verts[f.vertex[1]],
           c = aMesh.mMesh.verts[f.vertex[2]];
      Vec3 d = (a+b+c) / 3.0;
      float t,u,v;
      if (RayTriangleIntersect(p,vec, a,b,d, t,u,v)
       || RayTriangleIntersect(p,vec, a,d,b, t,u,v))
      {
         hitEdge = OrderEdge(f.vertex[0], f.vertex[1]);
      }
      else if (RayTriangleIntersect(p,vec, b,c,d, t,u,v)
            || RayTriangleIntersect(p,vec, b,d,c, t,u,v))
      {
         hitEdge = OrderEdge(f.vertex[1], f.vertex[2]);
      }
      else
      {
         hitEdge = OrderEdge(f.vertex[0], f.vertex[2]);
      }
   }
   return hitEdge;
}

void VertexSelector::SelectEdgePatch(Vec3      p,
                                     Vec3      v,
                                     MeshView& aMesh)
{
   Edge e = GetHitEdge(p,v,aMesh);
   if (e.first != -1)
   {
      std::vector<Edge> edgePatch;
      MeshAlgorithm::CreaseEdgePatch(aMesh.mMesh.GetCreaseMesh(), aMesh.GetMeshAdjacency(), e, edgePatch);
      aMesh.mSelectedEdges = edgePatch;
   }
}

void VertexSelector::SelectInterface(Vec3      p,
                                     Vec3      v,
                                     MeshView& aMesh)
{
   int hitFace = GetHitFace(p,v,aMesh);
   if (hitFace >= 0)
   {
      Face& f = aMesh.mMesh.crease_faces[hitFace];
      IndexList startFaces, patchFaces;
      startFaces.push_back(hitFace);
      MeshAlgorithm::FillRegion(aMesh.GetMeshAdjacency(), startFaces, patchFaces);
      aMesh.SetSelectedFaces(patchFaces);
   }
   else
   {
      aMesh.ClearSelection();
   }
}

void VertexSelector::DrawWindow()
{
   DrawBox();
}

void VertexSelector::DrawBox()
{
   if (mBoxStarted)
   {
      gl::NoLighting state1;
      glLineWidth(1);
      glColor3f(0,0,0);
      glEnable(GL_LINE_STIPPLE);
      glLineStipple(1, 0xf0f0);
      glBegin(GL_LINE_LOOP);
      glVertex3f(mDownPoint.x, mDownPoint.y, 0);
      glVertex3f(mDownPoint.x, mUpPoint.y, 0);
      glVertex3f(mUpPoint.x, mUpPoint.y, 0);
      glVertex3f(mUpPoint.x, mDownPoint.y, 0);
      glEnd();
      glColor3f(1,1,1);
      glLineStipple(1, 0x0f0f);
      glBegin(GL_LINE_LOOP);
      glVertex3f(mDownPoint.x, mDownPoint.y, 0);
      glVertex3f(mDownPoint.x, mUpPoint.y, 0);
      glVertex3f(mUpPoint.x, mUpPoint.y, 0);
      glVertex3f(mUpPoint.x, mDownPoint.y, 0);
      glEnd();
      glDisable(GL_LINE_STIPPLE);
      glLineWidth(1);
   }
}

//
//int VertexListEntity::Handle(int event)
//{
//   int r = 0;
//   if (mBoxSelect == 0)
//   {
//      if (event == FL_KEYDOWN)
//      {
//         if (Fl::event_key() == 'b' && !mBoxSelect)
//         {
//            mBoxSelect = new VertexSelector();
//            mBoxSelect->mSelectType = VertexSelector::cNORMAL;
//            if (Fl::event_ctrl())
//            {
//               mBoxSelect->mSelectType = VertexSelector::cAND;
//            }
//            else if (Fl::event_shift())
//            {
//               mBoxSelect->mSelectType = VertexSelector::cOR;
//            }
//            mScene->AddEntity(mBoxSelect);
//            r = 1;
//         }
//         else if (Fl::event_key() == 'a')
//         {
//            if (mSelectedVerts.empty())
//            {
//               size_t count = mVerts->size();
//               mSelectedVerts.resize(count);
//               for (size_t i = 0; i < mSelectedVerts.size(); ++i)
//               {
//                  mSelectedVerts[i] = i;
//               }
//               UpdateCentroid();
//            }
//            else
//            {
//               mSelectedVerts.clear();
//            }
//            DrawEvents::Redraw();
//         }
//      }
//   }
//   else if (mBoxSelect)
//   {
//      r = mBoxSelect->handle(event);
//      if (r)
//      {
//         DrawEvents::Redraw();
//      }
//      if (r == 2)
//      {
//         mBoxSelect->mDownPoint.y = mWindow->h() - mBoxSelect->mDownPoint.y;
//         mBoxSelect->mUpPoint.y = mWindow->h() - mBoxSelect->mUpPoint.y;
//         Box selBox(mBoxSelect->mDownPoint, mBoxSelect->mUpPoint);
//         float xScale(mWindow->w() / 2.0f), yScale(mWindow->h()/2.0f);
//         selBox.min_x = selBox.min_x / xScale - 1;
//         selBox.max_x = selBox.max_x / xScale - 1;
//         selBox.min_y = selBox.min_y / yScale - 1;
//         selBox.max_y = selBox.max_y / yScale - 1;
//
//         Mat4 worldToScreen = mScene->mTransform.GetFullTransform();
//         std::vector<int> selection;
//         size_t vertCount(mVerts->size());
//         const VertexList& verts = mVerts->Get();
//         for (size_t i = 0; i < vertCount; ++i)
//         {
//            const Vec3 v = verts[i];
//            Vec3 sv = v * worldToScreen;
//
//            if (selBox.Contains(Vec2(sv.x, sv.y)))
//            {
//               selection.push_back(i);
//            }
//         }
//         if (mBoxSelect->mSelectType == VertexSelector::cNORMAL)
//         {
//            mSelectedVerts = selection;
//         }
//         else if (mBoxSelect->mSelectType == VertexSelector::cAND)
//         {
//            std::vector<int> final;
//            std::set_intersection(selection.begin(), selection.end(), mSelectedVerts.begin(), mSelectedVerts.end(), std::back_inserter(final));
//            mSelectedVerts = final;
//         }
//         else
//         {
//            std::vector<int> final;
//            std::set_union(selection.begin(), selection.end(), mSelectedVerts.begin(), mSelectedVerts.end(), std::back_inserter(final));
//            mSelectedVerts = final;
//         }
//         UpdateCentroid();
//
//         mScene->RemoveEntity(mBoxSelect);
//         mBoxSelect = 0;
//      }
//   }
//   return r;
//}
//
//void VertexListEntity::Draw()
//{
//   glEnable(GL_POINT_SMOOTH);
//   gl::NoDepthTest format1;
//   gl::NoLighting  format2;
//   VertexAccessor& v = *mVerts;
//
//   glPointSize(3.0);
//   glColor3f(0,0,0);
//   glBegin(GL_POINTS);
//   for (size_t i = 0; i < mSelectedVerts.size(); ++i)
//   {
//      const Vec3& vec = v[mSelectedVerts[i]];
//      gl::Vertex(vec);
//   }
//   glEnd();
//
//   glPointSize(1.0);
//   glColor3f(1,1,1);
//
//   glBegin(GL_POINTS);
//   for (size_t i = 0; i < mSelectedVerts.size(); ++i)
//   {
//      const Vec3& vec = v[mSelectedVerts[i]];
//      gl::Vertex(vec);
//   }
//   glEnd();
//}
//
//void VertexListEntity::UpdateCentroid()
//{
//   if (mSelectedVerts.empty())
//   {
//      return;
//   }
//   Vec3 sum;
//
//   for (size_t i = 0; i < mSelectedVerts.size(); ++i)
//   {
//      sum += (*mVerts)[mSelectedVerts[i]];
//   }
//   mCentroid = sum / mSelectedVerts.size();
//   CentroidUpdated();
//}
//
//void VertexListEntity::ListUpdated()
//{
//   mSelectedVerts.clear();
//}
//
//VertexListEntity::VertexListEntity(ViewScene*      aScene,
//                                   VertexAccessor* aVerts,
//                                   GlWindow*       aWindowPtr) 
//                                   : mWindow(aWindowPtr), mScene(aScene), mBoxSelect(0), mVerts(aVerts)
//{
//   mVerts->ListUpdated.connect(boost::bind(&VertexListEntity::ListUpdated, this));
//}
void VertexSelector::SelectAll()
{
   MeshView* view = ViewScene::GetInstance().GetSelectedMesh();
   if (view)
   {
      if (ViewScene::GetInstance().GetSelectionMode() == ViewScene::cVERTEX)
      {
         std::vector<int>& verts = view->GetSelectedVertices();
         if (verts.size() > 0)
         {
            verts.clear();
            view->SelectionUpdated();
         }
         else
         {
            view->SelectAllVertices();
         }
      }
      else if (ViewScene::GetInstance().GetSelectionMode() == ViewScene::cFACE)
      {
         if (! view->GetSelectedFaces().empty())
         {
            view->SetSelectedFaces(IndexList());
         }
         else
         {
            IndexList allFaces(boost::counting_iterator<int>(0), boost::counting_iterator<int>(view->mMesh.crease_faces.size()));
            view->SetSelectedFaces(allFaces);
         }
         
      }
      else if (ViewScene::GetInstance().GetSelectionMode() == ViewScene::cLOOP)
      {
         if (view->mSelectedLoops.size() > 0)
         {
            view->mSelectedLoops.clear();
         }
         else
         {
            for (size_t i = 0; i < view->GetCreases().mLoops.size(); ++i)
            {
               view->mSelectedLoops.push_back(i);
            }
         }
         view->SelectionUpdated();
      }
   }
}